package com.ipan.jfinal.service.impl;

import java.util.List;
import java.util.Map;

import com.ipan.jfinal.service.BaseService;
import com.ipan.jfinal.service.ServiceException;
import com.ipan.kits.reflect.ReflectionUtil;
import com.ipan.poi.excel.config.XlsEntity;
import com.ipan.poi.excel.config.XlsProperty;
import com.ipan.poi.excel.service.MatchType;
import com.jfinal.aop.Before;
import com.jfinal.plugin.activerecord.DaoTemplate;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.SqlPara;
import com.jfinal.plugin.activerecord.Table;
import com.jfinal.plugin.activerecord.TableMapping;
import com.jfinal.plugin.activerecord.tx.Tx;

/**
 * Service基类
 * 
 * 目的：将系统的业务层锁定在Service层，否则Model、Db都可以取操作数据库，自由度太大，容易破坏规则！
 * 封装了Model方式操作数据库的基本方法。
 * 要统一所有Service的接口，子类继承后就具备这些接口的能力，那么必须有DAO具体实现；
 * 所以，这里创建了一个子类Model来充当DAO，这样子类才能具备接口定义的能力；
 * 
 * @author iPan
 * @version 2018-02-13
 */
@SuppressWarnings("rawtypes")
public class BaseServiceImpl<T extends Model> implements BaseService<T> {
	
	protected Class<T> modelClass;
	protected T model = null; // 这个模型是充当DAO使用的，所以就当成是DAO吧！
	
	@SuppressWarnings("unchecked")
	public BaseServiceImpl() {
		if (ReflectionUtil.isProxyClass(this.getClass())) {
			this.modelClass = (Class<T>) ReflectionUtil.getSuperClassGenricType(this.getClass().getSuperclass());
		} else {
			this.modelClass = (Class<T>) ReflectionUtil.getSuperClassGenricType(this.getClass());
		}
		model = newModel();
	}
	
	protected String getTableName() {
		return getTableName(this.modelClass);
	}
	
	protected Table getTable() {
		return getTable(this.modelClass);
	}
	
	protected static String getTableName(Class<? extends Model> modelClass) {
		Table tab = TableMapping.me().getTable(modelClass);
		return (tab != null) ? tab.getName() : null;
	}
	
	protected static Table getTable(Class<? extends Model> modelClass) {
		return TableMapping.me().getTable(modelClass);
	}
	
	@Override
	public T newModel() {
		T obj = null;
		try {
			// 实体类必须有默认构造器
			obj = this.modelClass.newInstance();
		} catch (Exception e) {
			throw new ServiceException("创建实体类出错！", e);
		}
		return obj;
	}

	//--- 增 ---//
	@Before(Tx.class)
	@Override
	public boolean save(T obj) {
		return obj.save();
	}

	//--- 删 ---//
	@Before(Tx.class)
	@Override
	public boolean delete(T obj) {
		return obj.delete();
	}

	@Before(Tx.class)
	@Override
	public boolean deleteById(Object... idValues) {
		String[] primarkKey = this.getTable().getPrimaryKey();
		if (primarkKey == null || primarkKey.length == 0) {
			throw new ServiceException("Table映射未配置");
		}
		if (primarkKey.length > 1) {
			return model.deleteByIds(idValues);
		} else {
			return model.deleteById(idValues[0]);
		}
	}
	@Before(Tx.class)
	@Override
	public boolean deleteById(Object idValue) {
		return model.deleteByIds(idValue);
	}

	//--- 改 ---//
	@Before(Tx.class)
	@Override
	public boolean update(T obj) {
		return obj.update();
	}

	//--- 查 ---//
	@Override
	@SuppressWarnings("unchecked")
	public T findById(Object... idValues) {
		String[] primarkKey = this.getTable().getPrimaryKey();
		if (primarkKey == null || primarkKey.length == 0) {
			throw new ServiceException("Table映射未配置");
		}
		if (primarkKey.length > 1) {
			return (T) model.findById(idValues);
		} else {
			return (T) model.findById(idValues[0]);
		}
	}
	@Override
	@SuppressWarnings("unchecked")
	public T findById(Object idValue) {
		return (T) model.findById(idValue);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findByIdLoadColumns(Object idValue, String columns) {
		return (T) model.findByIdLoadColumns(idValue, columns);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findByIdLoadColumns(Object[] idValues, String columns) {
		return (T) model.findByIdLoadColumns(idValues, columns);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(String sql) {
		return model.find(sql);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(String sql, Object... paras) {
		return model.find(sql, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(SqlPara sqlPara) {
		return model.find(sqlPara);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByCache(String cacheName, Object key, String sql) {
		return model.findByCache(cacheName, key, sql);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByCache(String cacheName, Object key, String sql, Object... paras) {
		return model.findByCache(cacheName, key, sql, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findFirst(String sql, Object... paras) {
		return (T) model.findFirst(sql, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findFirst(String sql) {
		return (T) model.findFirst(sql);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findFirstByCache(String cacheName, Object key, String sql, Object... paras) {
		return (T) model.findFirstByCache(cacheName, key, sql, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findFirstByCache(String cacheName, Object key, String sql) {
		return (T) model.findFirstByCache(cacheName, key, sql);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findFirst(SqlPara sqlPara) {
		return (T) model.findFirst(sqlPara);
	}
	
	//--- 分页查询 ---//
	@Override
	@SuppressWarnings("unchecked")
	public Page<T> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
		return model.paginate(pageNumber, pageSize, select, sqlExceptSelect, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect) {
		return model.paginate(pageNumber, pageSize, select, sqlExceptSelect);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginate(int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect,
			Object... paras) {
		return model.paginate(pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginateByFullSql(int pageNumber, int pageSize, String totalRowSql, String findSql, Object... paras) {
		return model.paginateByFullSql(pageNumber, pageSize, totalRowSql, findSql, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginateByFullSql(int pageNumber, int pageSize, boolean isGroupBySql, String totalRowSql, String findSql,
			Object... paras) {
		return model.paginateByFullSql(pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect, 
			Object... paras) {
		return model.paginateByCache(cacheName, key, pageNumber, pageSize, select, sqlExceptSelect, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect) {
		return model.paginateByCache(cacheName, key, pageNumber, pageSize, select, sqlExceptSelect);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql,
			String select, String sqlExceptSelect, Object... paras) {
		return model.paginateByCache(cacheName, key, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> paginate(int pageNumber, int pageSize, SqlPara sqlPara) {
		return model.paginate(pageNumber, pageSize, sqlPara);
	}

	// --- 支持ipan_poi导入导出 --- //
	private Object getSqlValue(Object value) {
		Object result = value;
		if (result instanceof String) {
			result = "'" + value + "'";
		}
		return result;
	}
	/**
	 * 返回验证的记录条数（导入）
	 * 根据配置文件的配置，查询对应实体的条数；
	 */
	@Override
	public List<T> selectXls(XlsEntity defEntity, T entity) {
		// 获取需要验证的字段
		List<XlsProperty> validColumnList = defEntity.getValidProperty();
		Table table = TableMapping.me().getTable(entity.getClass());
		StringBuilder buf = new StringBuilder();
		buf.append("select * from ").append(table.getName()).append(" where ");
		for (int i = 0, len = validColumnList.size(); i < len; ++i) {
			String name = validColumnList.get(i).getName();
			Object value = ReflectionUtil.getProperty(entity, name);
			if (i > 0) {
				buf.append(" and ");
			}
			
			buf.append(name).append("=").append(getSqlValue(value));
		}
		return entity.find(buf.toString());
	}

	/**
	 * 插入实体到数据库（导入）
	 * 根据配置文件配置的字段，插入到数据库；
	 * 未配置的字段如何处理，需要根据各自项目的情况，自行实现该接口；默认JDBC实现是不会处理的；
	 */
	@Override
	public void insertXls(XlsEntity defEntity, T entity) {
		entity.save();
	}

	/**
	 * 更新实体到数据库（导入）
	 * 根据配置文件的配置，更新对应实体；
	 */
	@Override
	public void updateXls(XlsEntity defEntity, T entity) {
		entity.update();
	}

	/**
	 * 查询实体列表（导出）
	 * 根据配置文件配置的查询字段，查询该实体；
	 * 默认JDBC实现，查询配置的待查询字段的并集，如果字段为空就排除该查询字段；查询过滤取决于配合的待查询字段以及实体类的值；
	 * @see com.ipan.poi.excel.service.DefaultXlsService 参考默认实现；
	 */
	@Override
	public List<T> searchXls(XlsEntity defEntity, T entity) {
		List<XlsProperty> searchColumnList = defEntity.getSearchProperty();
		Table table = TableMapping.me().getTable(entity.getClass());
		StringBuilder buf = new StringBuilder();
		buf.append("select * from ").append(table.getName()).append(" where ");
		for (int i = 0, len = searchColumnList.size(); i < len; ++i) {
			String name = searchColumnList.get(i).getName();
			String search = searchColumnList.get(i).getSearch().toUpperCase();
			Object value = ReflectionUtil.getProperty(entity, name);
			// 排除值为空的情况
			if (value == null || (value instanceof CharSequence && ((CharSequence)value).length() < 1)) {
				continue;
			}
			if (i > 0) {
				buf.append(" and ");
			}
			MatchType type = MatchType.valueOf(search);
			if (MatchType.EQ.equals(type)) {
				buf.append(name).append("=").append(getSqlValue(value));
			} else if (MatchType.LIKE.equals(type)) {
				buf.append(name).append(" like ").append(getSqlValue(value));
			} else if (MatchType.LT.equals(type)) {
				buf.append(name).append("<").append(value);
			} else if (MatchType.GT.equals(type)) {
				buf.append(name).append(">").append(value);
			} else if (MatchType.LE.equals(type)) {
				buf.append(name).append("<=").append(value);
			} else if (MatchType.GE.equals(type)) {
				buf.append(name).append(">=").append(value);
			}
		}
		return entity.find(buf.toString());
	}

	@Override
	public DaoTemplate<T> template(String key, Map data) {
		return model.template(key, data);
	}

	@Override
	public DaoTemplate<T> template(String key, Object... paras) {
		return model.template(key, paras);
	}

	@Override
	public DaoTemplate<T> template(String key, Model model) {
		return model.template(key, model);
	}

	@Override
	public DaoTemplate<T> templateByString(String content, Map data) {
		return model.templateByString(content, data);
	}

	@Override
	public DaoTemplate<T> templateByString(String content, Object... paras) {
		return model.templateByString(content, paras);
	}

	@Override
	public DaoTemplate<T> templateByString(String content, Model model) {
		return model.templateByString(content, model);
	}
	
}
